1
2
3
4
5
6 package ch.twiddlefinger.inet.rewinder.model.parser.conversion;
7
8 import java.io.IOException;
9 import java.io.ObjectInputStream;
10 import java.io.ObjectOutputStream;
11 import java.io.Serializable;
12
13 import java.util.AbstractCollection;
14 import java.util.AbstractSet;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.ListIterator;
18 import java.util.Map;
19 import java.util.NoSuchElementException;
20 import java.util.Set;
21
22
23 /***
24 * <p> This class represents a <code>Map</code> collection with real-time
25 * behavior. Unless the map's size exceeds its current capacity,
26 * no dynamic memory allocation is ever performed and response time is
27 * <b>extremely fast</b> and <b>consistent</b>.</p>
28 *
29 * <p> Our <a href="http://jade.dautelle.com/doc/benchmark.txt">benchmark</a>
30 * indicates that {@link FastMap#put FastMap.put(key, value)} is up to
31 * <b>5x faster</b> than <code>java.util.HashMap.put(key, value)</code>.
32 * This difference is mostly due to the cost of the <code>Map.Entry</code>
33 * allocations that {@link FastMap} avoids by recycling its entries
34 * (see note below).</p>
35 *
36 * <p> {@link FastMap} has a predictable iteration order, which is the order
37 * in which keys were inserted into the map (similar to
38 * <code>java.util.LinkedHashMap</code> collection class).
39 * A bi-directional list iterator over the map entries is also
40 * {@link #fastIterator provided}, this iterator can be moved
41 * to the {@link FastIterator#toFirst first} or to the
42 * {@link FastIterator#toLast last} entry for unlimited reuse.</p>
43 *
44 * <p> Applications may change the resizing policy of {@link FastMap}
45 * by overriding the {@link #sizeChanged} method. For example, to reduce
46 * memory footprint, the map's capacity could be maitained at ±50% of
47 * the current map's size.</p>
48 *
49 * <p> This implementation is not synchronized. Multiple threads accessing
50 * or modifying the collection must be synchronized externally.</p>
51 *
52 * <p> <b>Note:</b> To avoid dynamic memory allocations, {@link FastMap}
53 * maintains an internal pool of <code>Map.Entry</code> objects. The size
54 * of the pool is determined by the map's capacity. When an entry is
55 * removed from the map, it is automatically restored to the pool.</p>
56 *
57 * <p><i> This class is <b>public domain</b> (not copyrighted).</i></p>
58 *
59 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
60 * @version 6.0, January 18 2004
61 */
62 public class FastMap implements Map, Cloneable, Serializable {
63 /***
64 * Holds the map's hash table.
65 */
66 private transient EntryImpl[] _entries;
67
68 /***
69 * Holds the map's current capacity.
70 */
71 private transient int _capacity;
72
73 /***
74 * Holds the hash code mask.
75 */
76 private transient int _mask;
77
78 /***
79 * Holds the first pool entry (linked list).
80 */
81 private transient EntryImpl _poolFirst;
82
83 /***
84 * Holds the first map entry (linked list).
85 */
86 private transient EntryImpl _mapFirst;
87
88 /***
89 * Holds the last map entry (linked list).
90 */
91 private transient EntryImpl _mapLast;
92
93 /***
94 * Holds the current size.
95 */
96 private transient int _size;
97 private final FastIterator _fastIterator = new FastIterator();
98 private transient Values _values;
99 private transient EntrySet _entrySet;
100 private transient KeySet _keySet;
101
102 /***
103 * Creates a {@link FastMap} with a capacity of <code>16</code> entries.
104 */
105 public FastMap() {
106 initialize(16);
107 }
108
109 /***
110 * Creates a {@link FastMap}, copy of the specified <code>Map</code>.
111 * If the specified map is not an instance of {@link FastMap}, the
112 * newly created map has a capacity set to the specified map's size.
113 * The copy has the same order as the original, regardless of the original
114 * map's implementation:<pre>
115 * TreeMap dictionary = ...;
116 * FastMap dictionaryLookup = new FastMap(dictionary);
117 * </pre>
118 *
119 * @param map the map whose mappings are to be placed in this map.
120 */
121 public FastMap(Map map) {
122 int capacity = (map instanceof FastMap) ? ((FastMap) map).capacity()
123 : map.size();
124 initialize(capacity);
125 putAll(map);
126 }
127
128 /***
129 * Creates a {@link FastMap} with the specified capacity. Unless the
130 * capacity is exceeded, operations on this map do not allocate entries.
131 * For optimum performance, the capacity should be of the same order
132 * of magnitude or larger than the expected map's size.
133 *
134 * @param capacity the number of buckets in the hash table; it also
135 * defines the number of pre-allocated entries.
136 */
137 public FastMap(int capacity) {
138 initialize(capacity);
139 }
140
141 /***
142 * Returns the number of key-value mappings in this {@link FastMap}.
143 *
144 * @return this map's size.
145 */
146 public int size() {
147 return _size;
148 }
149
150 /***
151 * Returns the capacity of this {@link FastMap}. The capacity defines
152 * the number of buckets in the hash table, as well as the maximum number
153 * of entries the map may contain without allocating memory.
154 *
155 * @return this map's capacity.
156 */
157 public int capacity() {
158 return _capacity;
159 }
160
161 /***
162 * Indicates if this {@link FastMap} contains no key-value mappings.
163 *
164 * @return <code>true</code> if this map contains no key-value mappings;
165 * <code>false</code> otherwise.
166 */
167 public boolean isEmpty() {
168 return _size == 0;
169 }
170
171 /***
172 * Indicates if this {@link FastMap} contains a mapping for the specified
173 * key.
174 *
175 * @param key the key whose presence in this map is to be tested.
176 * @return <code>true</code> if this map contains a mapping for the
177 * specified key; <code>false</code> otherwise.
178 * @throws NullPointerException if the key is <code>null</code>.
179 */
180 public boolean containsKey(Object key) {
181 EntryImpl entry = _entries[keyHash(key) & _mask];
182
183 while (entry != null) {
184 if (key.equals(entry._key)) {
185 return true;
186 }
187
188 entry = entry._next;
189 }
190
191 return false;
192 }
193
194 /***
195 * Indicates if this {@link FastMap} maps one or more keys to the
196 * specified value.
197 *
198 * @param value the value whose presence in this map is to be tested.
199 * @return <code>true</code> if this map maps one or more keys to the
200 * specified value.
201 * @throws NullPointerException if the key is <code>null</code>.
202 */
203 public boolean containsValue(Object value) {
204 EntryImpl entry = _mapFirst;
205
206 while (entry != null) {
207 if (value.equals(entry._value)) {
208 return true;
209 }
210
211 entry = entry._after;
212 }
213
214 return false;
215 }
216
217 /***
218 * Returns the value to which this {@link FastMap} maps the specified key.
219 *
220 * @param key the key whose associated value is to be returned.
221 * @return the value to which this map maps the specified key,
222 * or <code>null</code> if there is no mapping for the key.
223 * @throws NullPointerException if key is <code>null</code>.
224 */
225 public Object get(Object key) {
226 EntryImpl entry = _entries[keyHash(key) & _mask];
227
228 while (entry != null) {
229 if (key.equals(entry._key)) {
230 return entry._value;
231 }
232
233 entry = entry._next;
234 }
235
236 return null;
237 }
238
239 /***
240 * Returns the entry with the specified key.
241 *
242 * @param key the key whose associated entry is to be returned.
243 * @return the entry for the specified key or <code>null</code> if none.
244 */
245 public Entry getEntry(Object key) {
246 EntryImpl entry = _entries[keyHash(key) & _mask];
247
248 while (entry != null) {
249 if (key.equals(entry._key)) {
250 return entry;
251 }
252
253 entry = entry._next;
254 }
255
256 return null;
257 }
258
259 /***
260 * Associates the specified value with the specified key in this
261 * {@link FastMap}. If the {@link FastMap} previously contained a mapping
262 * for this key, the old value is replaced.
263 *
264 * @param key the key with which the specified value is to be associated.
265 * @param value the value to be associated with the specified key.
266 * @return the previous value associated with specified key,
267 * or <code>null</code> if there was no mapping for key.
268 * A <code>null</code> return can also indicate that the map
269 * previously associated <code>null</code> with the specified key.
270 * @throws NullPointerException if the key is <code>null</code>.
271 */
272 public Object put(Object key, Object value) {
273 EntryImpl entry = _entries[keyHash(key) & _mask];
274
275 while (entry != null) {
276 if (key.equals(entry._key)) {
277 Object prevValue = entry._value;
278 entry._value = value;
279
280 return prevValue;
281 }
282
283 entry = entry._next;
284 }
285
286
287 addEntry(key, value);
288
289 return null;
290 }
291
292 /***
293 * Returns a reusable {@link FastIterator} over this {@link FastMap} entries
294 * (unique instance per map). For example:<pre>
295 * // Iteration without memory allocation!
296 * for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
297 * Entry entry = i.nextEntry();
298 * ...
299 * }</pre>
300 *
301 * @return an iterator which can be reset for reuse over and over.
302 * @see FastMap.FastIterator
303 */
304 public FastIterator fastIterator() {
305 return _fastIterator;
306 }
307
308 /***
309 * Copies all of the mappings from the specified map to this
310 * {@link FastMap}.
311 *
312 * @param map the mappings to be stored in this map.
313 * @throws NullPointerException the specified map is <code>null</code>, or
314 * the specified map contains <code>null</code> keys.
315 */
316 public void putAll(Map map) {
317 for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
318 Entry e = (Entry) i.next();
319 addEntry(e.getKey(), e.getValue());
320 }
321 }
322
323 /***
324 * Removes the mapping for this key from this {@link FastMap} if present.
325 *
326 * @param key the key whose mapping is to be removed from the map.
327 * @return previous value associated with specified key,
328 * or <code>null</code> if there was no mapping for key.
329 * A <code>null</code> return can also indicate that the map
330 * previously associated <code>null</code> with the specified key.
331 * @throws NullPointerException if the key is <code>null</code>.
332 */
333 public Object remove(Object key) {
334 EntryImpl entry = _entries[keyHash(key) & _mask];
335
336 while (entry != null) {
337 if (key.equals(entry._key)) {
338 Object prevValue = entry._value;
339 removeEntry(entry);
340
341 return prevValue;
342 }
343
344 entry = entry._next;
345 }
346
347 return null;
348 }
349
350 /***
351 * Removes all mappings from this {@link FastMap}.
352 */
353 public void clear() {
354
355 for (EntryImpl entry = _mapFirst; entry != null;
356 entry = entry._after) {
357 entry._key = null;
358 entry._value = null;
359 entry._before = null;
360 entry._next = null;
361
362 if (entry._previous == null) {
363 _entries[entry._index] = null;
364 } else {
365 entry._previous = null;
366 }
367 }
368
369
370 if (_mapLast != null) {
371 _mapLast._after = _poolFirst;
372 _poolFirst = _mapFirst;
373 _mapFirst = null;
374 _mapLast = null;
375 _size = 0;
376 sizeChanged();
377 }
378 }
379
380 /***
381 * Changes the current capacity of this {@link FastMap}. If the capacity
382 * is increased, new entries are allocated and added to the pool.
383 * If the capacity is decreased, entries from the pool are deallocated
384 * (and are garbage collected eventually). The capacity also determined
385 * the number of buckets for the hash table.
386 *
387 * @param newCapacity the new capacity of this map.
388 */
389 public void setCapacity(int newCapacity) {
390 if (newCapacity > _capacity) {
391
392 for (int i = _capacity; i < newCapacity; i++) {
393 EntryImpl entry = new EntryImpl();
394 entry._after = _poolFirst;
395 _poolFirst = entry;
396 }
397 } else if (newCapacity < _capacity) {
398
399 for (int i = newCapacity; (i < _capacity) && (_poolFirst != null);
400 i++) {
401
402 EntryImpl entry = _poolFirst;
403 _poolFirst = entry._after;
404 entry._after = null;
405 }
406 }
407
408
409 int tableLength = 16;
410
411 while (tableLength < newCapacity) {
412 tableLength <<= 1;
413 }
414
415
416 if (_entries.length != tableLength) {
417 _entries = new EntryImpl[tableLength];
418 _mask = tableLength - 1;
419
420
421 EntryImpl entry = _mapFirst;
422
423 while (entry != null) {
424 int index = keyHash(entry._key) & _mask;
425 entry._index = index;
426
427
428 entry._previous = null;
429
430 EntryImpl next = _entries[index];
431 entry._next = next;
432
433 if (next != null) {
434 next._previous = entry;
435 }
436
437 _entries[index] = entry;
438
439 entry = entry._after;
440 }
441 }
442
443 _capacity = newCapacity;
444 }
445
446 /***
447 * Returns a shallow copy of this {@link FastMap}. The keys and
448 * the values themselves are not cloned.
449 *
450 * @return a shallow copy of this map.
451 */
452 public Object clone() {
453 try {
454 FastMap clone = (FastMap) super.clone();
455 clone.initialize(_capacity);
456 clone.putAll(this);
457
458 return clone;
459 } catch (CloneNotSupportedException e) {
460
461 throw new InternalError();
462 }
463 }
464
465 /***
466 * Compares the specified object with this {@link FastMap} for equality.
467 * Returns <code>true</code> if the given object is also a map and the two
468 * maps represent the same mappings (regardless of collection iteration
469 * order).
470 *
471 * @param obj the object to be compared for equality with this map.
472 * @return <code>true</code> if the specified object is equal to this map;
473 * <code>false</code> otherwise.
474 */
475 public boolean equals(Object obj) {
476 if (obj == this) {
477 return true;
478 } else if (obj instanceof Map) {
479 Map that = (Map) obj;
480
481 if (this.size() == that.size()) {
482 EntryImpl entry = _mapFirst;
483
484 while (entry != null) {
485 if (!that.entrySet().contains(entry)) {
486 return false;
487 }
488
489 entry = entry._after;
490 }
491
492 return true;
493 } else {
494 return false;
495 }
496 } else {
497 return false;
498 }
499 }
500
501 /***
502 * Returns the hash code value for this {@link FastMap}.
503 *
504 * @return the hash code value for this map.
505 */
506 public int hashCode() {
507 int code = 0;
508 EntryImpl entry = _mapFirst;
509
510 while (entry != null) {
511 code += entry.hashCode();
512 entry = entry._after;
513 }
514
515 return code;
516 }
517
518 /***
519 * Returns a <code>String</code> representation of this {@link FastMap}.
520 *
521 * @return <code>this.entrySet().toString();</code>
522 */
523 public String toString() {
524 return entrySet().toString();
525 }
526
527 /***
528 * Returns a collection view of the values contained in this
529 * {@link FastMap}. The collection is backed by the map, so changes to
530 * the map are reflected in the collection, and vice-versa.
531 * The collection supports element removal, which removes the corresponding
532 * mapping from this map, via the
533 * <code>Iterator.remove</code>, <code>Collection.remove</code>,
534 * <code>removeAll</code>, <code>retainAll</code>,
535 * and <code>clear</code> operations. It does not support the
536 * <code>add</code> or <code>addAll</code> operations.
537 *
538 * @return a collection view of the values contained in this map.
539 */
540 public Collection values() {
541 return _values;
542 }
543
544 /***
545 * Returns a collection view of the mappings contained in this
546 * {@link FastMap}. Each element in the returned collection is a
547 * <code>Map.Entry</code>. The collection is backed by the map,
548 * so changes to the map are reflected in the collection, and vice-versa.
549 * The collection supports element removal, which removes the corresponding
550 * mapping from this map, via the
551 * <code>Iterator.remove</code>, <code>Collection.remove</code>,
552 * <code>removeAll</code>, <code>retainAll</code>,
553 * and <code>clear</code> operations. It does not support the
554 * <code>add</code> or <code>addAll</code> operations.
555 *
556 * @return a collection view of the mappings contained in this map.
557 */
558 public Set entrySet() {
559 return _entrySet;
560 }
561
562 /***
563 * Returns a set view of the keys contained in this {@link FastMap}.
564 * The set is backed by the map, so changes to the map are reflected
565 * in the set, and vice-versa. The set supports element removal,
566 * which removes the corresponding mapping from this map, via the
567 * <code>Iterator.remove</code>, <code>Collection.remove</code>,
568 * <code>removeAll</code>, <code>retainAll</code>,
569 * and <code>clear</code> operations. It does not support the
570 * <code>add</code> or <code>addAll</code> operations.
571 *
572 * @return a set view of the keys contained in this map.
573 */
574 public Set keySet() {
575 return _keySet;
576 }
577
578 /***
579 * This methods is being called when the size of this {@link FastMap}
580 * has changed. The default behavior is to double the map's capacity
581 * when the map's size exceeds the current map's capacity.
582 * Sub-class may override this method to implement custom resizing
583 * policies or to disable automatic resizing. For example:<pre>
584 * Map fixedCapacityMap = new FastMap(256) {
585 * protected sizeChanged() {
586 * // Do nothing, automatic resizing disabled.
587 * }
588 * };</pre>
589 * @see #setCapacity
590 */
591 protected void sizeChanged() {
592 if (size() > capacity()) {
593 setCapacity(capacity() * 2);
594 }
595 }
596
597 /***
598 * Returns the hash code for the specified key. The formula being used
599 * is identical to the formula used by <code>java.util.HashMap</code>
600 * (ensures similar behavior for ill-conditioned hashcode keys).
601 *
602 * @param key the key to calculate the hashcode for.
603 * @return the hash code for the specified key.
604 */
605 private static int keyHash(Object key) {
606
607 int hashCode = key.hashCode();
608 hashCode += ~(hashCode << 9);
609 hashCode ^= (hashCode >>> 14);
610 hashCode += (hashCode << 4);
611 hashCode ^= (hashCode >>> 10);
612
613 return hashCode;
614 }
615
616 /***
617 * Adds a new entry for the specified key and value.
618 * @param key the entry's key.
619 * @param value the entry's value.
620 */
621 private void addEntry(Object key, Object value) {
622 EntryImpl entry = _poolFirst;
623
624 if (entry != null) {
625 _poolFirst = entry._after;
626 entry._after = null;
627 } else {
628 entry = new EntryImpl();
629 }
630
631
632 entry._key = key;
633 entry._value = value;
634
635 int index = keyHash(key) & _mask;
636 entry._index = index;
637
638
639 EntryImpl next = _entries[index];
640 entry._next = next;
641
642 if (next != null) {
643 next._previous = entry;
644 }
645
646 _entries[index] = entry;
647
648
649 if (_mapLast != null) {
650 entry._before = _mapLast;
651 _mapLast._after = entry;
652 } else {
653 _mapFirst = entry;
654 }
655
656 _mapLast = entry;
657
658
659 _size++;
660 sizeChanged();
661 }
662
663 /***
664 * Removes the specified entry from the map.
665 *
666 * @param entry the entry to be removed.
667 */
668 private void removeEntry(EntryImpl entry) {
669
670 EntryImpl previous = entry._previous;
671 EntryImpl next = entry._next;
672
673 if (previous != null) {
674 previous._next = next;
675 entry._previous = null;
676 } else {
677 _entries[entry._index] = next;
678 }
679
680 if (next != null) {
681 next._previous = previous;
682 entry._next = null;
683 }
684
685
686
687 EntryImpl before = entry._before;
688 EntryImpl after = entry._after;
689
690 if (before != null) {
691 before._after = after;
692 entry._before = null;
693 } else {
694 _mapFirst = after;
695 }
696
697 if (after != null) {
698 after._before = before;
699 } else {
700 _mapLast = before;
701 }
702
703
704 entry._key = null;
705 entry._value = null;
706
707
708 entry._after = _poolFirst;
709 _poolFirst = entry;
710
711
712 _size--;
713 sizeChanged();
714 }
715
716 /***
717 * Initializes this instance for the specified capacity.
718 * Once initialized, operations on this map should not create new objects
719 * (unless the map's size exceeds the specified capacity).
720 *
721 * @param capacity the initial capacity.
722 */
723 private void initialize(int capacity) {
724
725 int tableLength = 16;
726
727 while (tableLength < capacity) {
728 tableLength <<= 1;
729 }
730
731
732 _entries = new EntryImpl[tableLength];
733 _mask = tableLength - 1;
734 _capacity = capacity;
735 _size = 0;
736
737
738 _values = new Values();
739 _entrySet = new EntrySet();
740 _keySet = new KeySet();
741
742
743 _poolFirst = null;
744 _mapFirst = null;
745 _mapLast = null;
746
747
748 for (int i = 0; i < capacity; i++) {
749 EntryImpl entry = new EntryImpl();
750 entry._after = _poolFirst;
751 _poolFirst = entry;
752 }
753 }
754
755 /***
756 * Requires special handling during de-serialization process.
757 *
758 * @param stream the object input stream.
759 * @throws IOException if an I/O error occurs.
760 * @throws ClassNotFoundException if the class for the object de-serialized
761 * is not found.
762 */
763 private void readObject(ObjectInputStream stream)
764 throws IOException, ClassNotFoundException {
765 int capacity = stream.readInt();
766 initialize(capacity);
767
768 int size = stream.readInt();
769
770 for (int i = 0; i < size; i++) {
771 Object key = stream.readObject();
772 Object value = stream.readObject();
773 addEntry(key, value);
774 }
775 }
776
777 /***
778 * Requires special handling during serialization process.
779 *
780 * @param stream the object output stream.
781 * @throws IOException if an I/O error occurs.
782 */
783 private void writeObject(ObjectOutputStream stream)
784 throws IOException {
785 stream.writeInt(_capacity);
786 stream.writeInt(_size);
787
788 int count = 0;
789 EntryImpl entry = _mapFirst;
790
791 while (entry != null) {
792 stream.writeObject(entry._key);
793 stream.writeObject(entry._value);
794 count++;
795 entry = entry._after;
796 }
797
798 if (count != _size) {
799 throw new IOException("FastMap Corrupted");
800 }
801 }
802
803 private class Values extends AbstractCollection {
804 public Iterator iterator() {
805 return new Iterator() {
806 EntryImpl after = _mapFirst;
807 EntryImpl before;
808
809 public void remove() {
810 if (before != null) {
811 removeEntry(before);
812 } else {
813 throw new IllegalStateException();
814 }
815 }
816
817 public boolean hasNext() {
818 return after != null;
819 }
820
821 public Object next() {
822 if (after != null) {
823 before = after;
824 after = after._after;
825
826 return before._value;
827 } else {
828 throw new NoSuchElementException();
829 }
830 }
831 };
832 }
833
834 public int size() {
835 return _size;
836 }
837
838 public boolean contains(Object o) {
839 return containsValue(o);
840 }
841
842 public void clear() {
843 FastMap.this.clear();
844 }
845 }
846
847 private class EntrySet extends AbstractSet {
848 public Iterator iterator() {
849 return new Iterator() {
850 EntryImpl after = _mapFirst;
851 EntryImpl before;
852
853 public void remove() {
854 if (before != null) {
855 removeEntry(before);
856 } else {
857 throw new IllegalStateException();
858 }
859 }
860
861 public boolean hasNext() {
862 return after != null;
863 }
864
865 public Object next() {
866 if (after != null) {
867 before = after;
868 after = after._after;
869
870 return before;
871 } else {
872 throw new NoSuchElementException();
873 }
874 }
875 };
876 }
877
878 public int size() {
879 return _size;
880 }
881
882 public boolean contains(Object obj) {
883
884 if (obj instanceof Entry) {
885 Entry entry = (Entry) obj;
886 Entry mapEntry = getEntry(entry.getKey());
887
888 return entry.equals(mapEntry);
889 } else {
890 return false;
891 }
892 }
893
894 public boolean remove(Object obj) {
895
896 if (obj instanceof Entry) {
897 Entry entry = (Entry) obj;
898 EntryImpl mapEntry = (EntryImpl) getEntry(entry.getKey());
899
900 if ((mapEntry != null) &&
901 (entry.getValue()).equals(mapEntry._value)) {
902 removeEntry(mapEntry);
903
904 return true;
905 }
906 }
907
908 return false;
909 }
910 }
911
912 private class KeySet extends AbstractSet {
913 public Iterator iterator() {
914 return new Iterator() {
915 EntryImpl after = _mapFirst;
916 EntryImpl before;
917
918 public void remove() {
919 if (before != null) {
920 removeEntry(before);
921 } else {
922 throw new IllegalStateException();
923 }
924 }
925
926 public boolean hasNext() {
927 return after != null;
928 }
929
930 public Object next() {
931 if (after != null) {
932 before = after;
933 after = after._after;
934
935 return before._key;
936 } else {
937 throw new NoSuchElementException();
938 }
939 }
940 };
941 }
942
943 public int size() {
944 return _size;
945 }
946
947 public boolean contains(Object obj) {
948
949 return FastMap.this.containsKey(obj);
950 }
951
952 public boolean remove(Object obj) {
953
954 return FastMap.this.remove(obj) != null;
955 }
956
957 public void clear() {
958 FastMap.this.clear();
959 }
960 }
961
962 /***
963 * This inner class represents a reusable list iterator over
964 * {@link FastMap} entries. This iterator is bi-directional and can be
965 * directly moved to the {@link #toFirst first} or {@link #toLast last}
966 * entry. For example:<pre>
967 * for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
968 * Entry entry = i.nextEntry();
969 * ...
970 * }</pre>
971 * {@link #set setting} or {@link #add adding} new entries is not
972 * supported.
973 */
974 public final class FastIterator implements ListIterator {
975 EntryImpl after = _mapFirst;
976 EntryImpl before;
977 int nextIndex = 0;
978
979 public FastIterator toFirst() {
980 after = _mapFirst;
981 before = null;
982 nextIndex = 0;
983
984 return this;
985 }
986
987 public FastIterator toLast() {
988 after = null;
989 before = _mapLast;
990 nextIndex = _size;
991
992 return this;
993 }
994
995 public boolean hasNext() {
996 return after != null;
997 }
998
999 public Entry nextEntry() {
1000 if (after != null) {
1001 nextIndex++;
1002 before = after;
1003 after = after._after;
1004
1005 return before;
1006 } else {
1007 throw new NoSuchElementException();
1008 }
1009 }
1010
1011 public Object next() {
1012 return nextEntry();
1013 }
1014
1015 public boolean hasPrevious() {
1016 return before != null;
1017 }
1018
1019 public Entry previousEntry() {
1020 if (before != null) {
1021 nextIndex--;
1022 after = before;
1023 before = before._after;
1024
1025 return after;
1026 } else {
1027 throw new NoSuchElementException();
1028 }
1029 }
1030
1031 public Object previous() {
1032 return previousEntry();
1033 }
1034
1035 public int nextIndex() {
1036 return nextIndex;
1037 }
1038
1039 public int previousIndex() {
1040 return nextIndex - 1;
1041 }
1042
1043 public void remove() {
1044 if (before != null) {
1045 removeEntry(before);
1046 } else {
1047 throw new IllegalStateException();
1048 }
1049 }
1050
1051 public void set(Object o) {
1052 throw new UnsupportedOperationException();
1053 }
1054
1055 public void add(Object o) {
1056 throw new UnsupportedOperationException();
1057 }
1058 }
1059
1060 /***
1061 * This class represents a {@link FastMap} entry.
1062 */
1063 private static final class EntryImpl implements Entry {
1064 /***
1065 * Holds the entry key (null when in pool).
1066 */
1067 private Object _key;
1068
1069 /***
1070 * Holds the entry value (null when in pool).
1071 */
1072 private Object _value;
1073
1074 /***
1075 * Holds the bucket index (undefined when in pool).
1076 */
1077 private int _index;
1078
1079 /***
1080 * Holds the previous entry in the same bucket (null when in pool).
1081 */
1082 private EntryImpl _previous;
1083
1084 /***
1085 * Holds the next entry in the same bucket (null when in pool).
1086 */
1087 private EntryImpl _next;
1088
1089 /***
1090 * Holds the entry added before this entry (null when in pool).
1091 */
1092 private EntryImpl _before;
1093
1094 /***
1095 * Holds the entry added after this entry
1096 * or the next available entry when in pool.
1097 */
1098 private EntryImpl _after;
1099
1100 /***
1101 * Returns the key for this entry.
1102 *
1103 * @return the entry's key.
1104 */
1105 public Object getKey() {
1106 return _key;
1107 }
1108
1109 /***
1110 * Returns the value for this entry.
1111 *
1112 * @return the entry's value.
1113 */
1114 public Object getValue() {
1115 return _value;
1116 }
1117
1118 /***
1119 * Sets the value for this entry.
1120 *
1121 * @param value the new value.
1122 * @return the previous value.
1123 */
1124 public Object setValue(Object value) {
1125 Object old = _value;
1126 _value = value;
1127
1128 return old;
1129 }
1130
1131 /***
1132 * Indicates if this entry is considered equals to the specified
1133 * entry.
1134 *
1135 * @param that the object to test for equality.
1136 * @return <code>true<code> if both entry are considered equal;
1137 * <code>false<code> otherwise.
1138 */
1139 public boolean equals(Object that) {
1140 if (that instanceof Entry) {
1141 Entry entry = (Entry) that;
1142
1143 return (_key.equals(entry.getKey())) &&
1144 ((_value != null) ? _value.equals(entry.getValue())
1145 : (entry.getValue() == null));
1146 } else {
1147 return false;
1148 }
1149 }
1150
1151 /***
1152 * Returns the hash code for this entry.
1153 *
1154 * @return this entry's hash code.
1155 */
1156 public int hashCode() {
1157 return _key.hashCode() ^
1158 ((_value != null) ? _value.hashCode() : 0);
1159 }
1160
1161 /***
1162 * Returns the text representation of this entry.
1163 *
1164 * @return this entry's textual representation.
1165 */
1166 public String toString() {
1167 return _key + "=" + _value;
1168 }
1169 }
1170 }